package com.ws.framework.util;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;

import com.ws.framework.ex.RemoteSourceException;

public abstract class HttpClient {

	protected static final Logger LOG = Logger.getLogger(HttpClient.class.getName());

	public static final String CONTENT_TYPE_JSON = "application/json";

	public static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";

	private static final String HTTP_METHOD_POST = "POST";

	private static final String HTTP_HEADER_CONTENT_TYPE = "Content-Type";

	private static final String HTTP_HEADER_CONTENT_LENGTH = "Content-Length";

	private static final String HTTP_HEADER_CONTENT_ENCODING = "Content-Encoding";

	private static final String ENCODING_GZIP = "gzip";

	private static final boolean FOLLOW_REDIRECTS = true;

	public static String sendByPost(String url, String json, int timeout) throws Exception {
		return sendByPost(url, json, timeout, 1, 0);
	}

	public static String sendByPost(String url, String json, int timeout, int retry) throws Exception {
		return sendByPost(url, json, timeout, retry, 0);
	}

	public static String sendByPost(String url, String json, int timeout, int retry, int interval) {
		RemoteSourceException exception = null;
		for (int i = 0; i < retry; i++) {
			try {
				URLConnection connection = new URL(url).openConnection(Proxy.NO_PROXY);
				if (!(connection instanceof HttpURLConnection)) {
					exception = new RemoteSourceException("Service URL [" + url + "] is not an HTTP URL");
					break;
				}
				HttpURLConnection con = (HttpURLConnection) connection;
				prepareConnection(con, json.length(), timeout);
				writeRequestBody(con, json);
				validateResponse(con);
				InputStream responseBody = readResponseBody(con);
				return readResult(responseBody);
			} catch (Exception ex) {
				if (ex instanceof RemoteSourceException)
					exception = (RemoteSourceException) ex;
				else
					exception = new RemoteSourceException(
							String.format("Request Source Server meet error. url:'%s', body:'%s'", url, json), ex);
				if (!(ex instanceof IOException)) {
					break;
				}
				if (interval > 0) {
					try {
						Thread.sleep(interval);
					} catch (InterruptedException e) {
						// ignore
					}
				}
			}
		}
		throw exception;
	}

	private static void prepareConnection(HttpURLConnection connection, int contentLength, int timeout)
			throws IOException {
		if (timeout >= 0) {
			connection.setConnectTimeout(timeout);
		}
		if (timeout >= 0) {
			connection.setReadTimeout(timeout);
		}
		// 系统自动处理跳转,对于多次跳转的只能处理一次
		connection.setInstanceFollowRedirects(FOLLOW_REDIRECTS);
		connection.setDoOutput(true);
		connection.setDoInput(true);
		connection.setRequestMethod(HTTP_METHOD_POST);
		connection.setUseCaches(false);
		connection.setRequestProperty(HTTP_HEADER_CONTENT_TYPE, CONTENT_TYPE_FORM);
		connection.setRequestProperty(HTTP_HEADER_CONTENT_LENGTH, Integer.toString(contentLength));
	}

	private static void writeRequestBody(HttpURLConnection con, String data) throws IOException {
		final DataOutputStream out = new DataOutputStream(con.getOutputStream());
		out.write(data.getBytes("UTF-8"));
		out.flush();
		out.close();
	}

	private static void validateResponse(HttpURLConnection con) throws Exception {
		if (con.getResponseCode() == 503) {
			String errorMsg = con.getHeaderField("errorCode");
			if (errorMsg != null && errorMsg.length() > 0) {
				LOG.log(Level.WARNING, "Source service handle meet error! Error Message is:" + errorMsg);
				throw new RemoteSourceException(errorMsg);
			} else {
				LOG.log(Level.INFO, "Source server handle meet error!");
				throw new RemoteSourceException("Source server handle meet error!");
			}
		}
		if (con.getResponseCode() >= 300) {
			LOG.log(Level.WARNING, "Did not receive successful HTTP response: status code = " + con.getResponseCode()
					+ ", status message = [" + con.getResponseMessage() + "]");
			throw new IOException("Did not receive successful HTTP response: status code = " + con.getResponseCode()
					+ ", status message = [" + con.getResponseMessage() + "]");
		}
	}

	private static InputStream readResponseBody(HttpURLConnection con) throws IOException {
		if (isGzipResponse(con)) {
			return new GZIPInputStream(con.getInputStream());
		} else {
			return con.getInputStream();
		}
	}

	private static boolean isGzipResponse(HttpURLConnection con) {
		String encodingHeader = con.getHeaderField(HTTP_HEADER_CONTENT_ENCODING);
		return encodingHeader != null && encodingHeader.toLowerCase().contains(ENCODING_GZIP);
	}

	private static String readResult(InputStream is) throws IOException, ClassNotFoundException {
		BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
		try {
			StringBuilder temp = new StringBuilder();
			String line = reader.readLine();
			while (line != null) {
				temp.append(line);
				line = reader.readLine();
			}
			return temp.toString();
		} finally {
			reader.close();
		}
	}
}
